package org.zjx.service.impl;

import lombok.Data;
import org.zjx.enums.prop.SlotStatus;
import org.zjx.enums.prop.SlotType;
import org.zjx.exception.InvalidItemOperationException;
import org.zjx.exception.ItemNotFoundException;
import org.zjx.exception.ItemOperationException;
import org.zjx.exception.SlotOperationException;
import org.zjx.model.prop.ItemInstance;
import org.zjx.model.prop.ItemSlot;
import org.zjx.model.prop.ItemTemplate;
import org.zjx.model.prop.SlotItem;
import org.zjx.repository.ItemInstanceRepository;
import org.zjx.repository.ItemSlotRepository;
import org.zjx.repository.ItemTemplateRepository;
import org.zjx.service.ItemManager;
import org.zjx.service.ItemStackService;
import org.zjx.service.ItemValidator;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

@Data
public class ItemManagerImpl implements ItemManager {

    // 依赖注入
    private final ItemInstanceRepository itemInstanceRepository;
    private final ItemSlotRepository itemSlotRepository;
    private final ItemTemplateRepository itemTemplateRepository;
    private final ItemStackService stackService;
    private final ItemValidator validator;

    // 缓存和锁
    private final Map<String, List<ItemInstance>> playerItemsCache = new ConcurrentHashMap<>();
    private final ReentrantLock globalLock = new ReentrantLock(true);

    public ItemManagerImpl(ItemInstanceRepository itemInstanceRepository,
                           ItemSlotRepository itemSlotRepository,
                           ItemTemplateRepository itemTemplateRepository,
                           ItemStackService stackService,
                           ItemValidator validator) {
        this.itemInstanceRepository = itemInstanceRepository;
        this.itemSlotRepository = itemSlotRepository;
        this.itemTemplateRepository = itemTemplateRepository;
        this.stackService = stackService;
        this.validator = validator;
    }

    // 锁操作接口
    @FunctionalInterface
    private interface LockableOperation<T> {
        T execute();
    }

    // 统一的锁操作方法
    private <T> T withLock(LockableOperation<T> operation) {
        globalLock.lock();
        try {
            return operation.execute();
        } finally {
            globalLock.unlock();
        }
    }

    // 缓存失效方法
    private void invalidatePlayerCache(String playerId) {
        withLock(() -> {
            playerItemsCache.remove(playerId);
            return null;
        });
    }

    // 批量更新道具并失效缓存
    private void updateItemsAndInvalidateCache(String playerId, List<ItemInstance> items) {
        withLock(() -> {
            if (!itemInstanceRepository.update(items)) {
                throw new ItemOperationException("Failed to update items");
            }
            invalidatePlayerCache(playerId);
            return null;
        });
    }

    // region ========== 基础查询方法 ==========

    @Override
    public List<ItemInstance> getPlayerItems(String playerId) {
        if (playerId == null) {
            return Collections.emptyList();
        }
        return withLock(() -> getFromCacheOrLoad(playerId));
    }

    private List<ItemInstance> getFromCacheOrLoad(String playerId) {
        List<ItemInstance> cachedItems = playerItemsCache.get(playerId);
        if (cachedItems != null) {
            return cachedItems;
        }

        return withLock(() -> {
            List<ItemInstance> items = playerItemsCache.get(playerId);
            if (items != null) {
                return items;
            }

            List<ItemInstance> loadedItems = Optional.ofNullable(
                            itemInstanceRepository.findByPlayerId(playerId))
                    .orElse(Collections.emptyList());

            playerItemsCache.put(playerId, loadedItems);
            return loadedItems;
        });
    }

    @Override
    public List<ItemSlot> getSlotDefinitions(SlotType slotType) {
        List<ItemSlot> slots = new ArrayList<>();
        for (int i = 0; i < slotType.getCapacity(); i++) {
            slots.add(createDefaultSlot(slotType, i));
        }
        return slots;
    }

    private ItemSlot createDefaultSlot(SlotType slotType, int position) {
        ItemSlot slot = new ItemSlot();
        slot.setSlotType(slotType);
        slot.setPosition(position);
        slot.setStatus(SlotStatus.NORMAL);
        return slot;
    }

    @Override
    public List<ItemInstance> getPlayerItemsBySlotType(String playerId, SlotType slotType) {
        return withLock(() -> {
            List<ItemInstance> playerItems = getPlayerItems(playerId);
            Map<Integer, ItemSlot> slotMap = getSlotMapByType(slotType);
            return buildSlotBasedItemList(playerItems, slotMap);
        });
    }

    private Map<Integer, ItemSlot> getSlotMapByType(SlotType slotType) {
        return itemSlotRepository.findByType(slotType).stream()
                .collect(Collectors.toMap(ItemSlot::getId, slot -> slot));
    }

    private List<ItemInstance> buildSlotBasedItemList(List<ItemInstance> playerItems,
                                                      Map<Integer, ItemSlot> slotMap) {
        List<ItemInstance> result = new ArrayList<>();
        for (ItemInstance instance : playerItems) {
            ItemSlot slot = slotMap.get(instance.getSlotId());
            if (slot != null) {
                while (result.size() <= slot.getPosition()) {
                    result.add(null);
                }
                result.set(slot.getPosition(), instance);
            }
        }
        return result;
    }

    private List<ItemSlot> getSortedSlots(SlotType slotType) {
        return itemSlotRepository.findByType(slotType).stream()
                .sorted(Comparator.comparingInt(ItemSlot::getPosition))
                .collect(Collectors.toList());
    }
    // endregion

    // region ========== 道具操作方法 ==========

    @Override
    public boolean moveItem(String playerId, int instanceId, int targetSlotId) {
        validateMoveParams(playerId, instanceId, targetSlotId);
        return withLock(() -> doMoveItem(playerId, instanceId, targetSlotId));
    }

    private boolean doMoveItem(String playerId, int instanceId, int targetSlotId) {
        List<ItemInstance> playerItems = getPlayerItems(playerId);
        ItemInstance sourceInstance = findPlayerItem(playerItems, instanceId);
        Optional<ItemInstance> targetInstance = findItemInSlot(playerItems, targetSlotId);

        validator.validateMoveOperation(sourceInstance, targetSlotId);

        if (targetInstance.isPresent() && tryStackItems(sourceInstance, targetInstance.get())) {
            return true;
        }

        performItemMove(sourceInstance, targetInstance, targetSlotId);
        return false;
    }

    private ItemInstance findPlayerItem(List<ItemInstance> playerItems, int instanceId) {
        return playerItems.stream()
                .filter(instance -> instanceId == instance.getId())
                .findFirst()
                .orElseThrow(() -> new ItemNotFoundException(instanceId+""));
    }

    private Optional<ItemInstance> findItemInSlot(List<ItemInstance> playerItems, int slotId) {
        return playerItems.stream()
                .filter(instance -> slotId == instance.getSlotId())
                .findFirst();
    }

    private boolean tryStackItems(ItemInstance source, ItemInstance target) {
        if (!stackService.canStack(source, target)) {
            return false;
        }
        if (stackService.handleStack(source, target)) {
            updateItemsAndInvalidateCache(source.getPlayerId(), Arrays.asList(source, target));
            return true;
        }
        throw new InvalidItemOperationException("Stack operation failed");
    }

    private void performItemMove(ItemInstance source, Optional<ItemInstance> target, int targetSlotId) {
        target.ifPresent(t -> t.setSlotId(source.getSlotId()));
        source.setSlotId(targetSlotId);
        List<ItemInstance> toUpdate = new ArrayList<>();
        toUpdate.add(source);
        target.ifPresent(toUpdate::add);
        updateItemsAndInvalidateCache(source.getPlayerId(), toUpdate);
    }

    @Override
    public boolean handOff(String playerId, SlotType slotType1, SlotType slotType2) {
        validateHandOffParams(playerId, slotType1, slotType2);
        return withLock(() -> doHandOff(playerId, slotType1, slotType2));
    }

    private boolean doHandOff(String playerId, SlotType slotType1, SlotType slotType2) {
        List<ItemSlot> slots1 = getSortedSlots(slotType1);
        List<ItemSlot> slots2 = getSortedSlots(slotType2);
        validateSlotCapacity(slots1, slots2);

        List<ItemInstance> playerItems = getPlayerItems(playerId);
        List<ItemInstance> updateData = new ArrayList<>();

        for (int i = 0; i < slots1.size(); i++) {
            swapItemsBetweenSlots(playerItems, slots1.get(i), slots2.get(i), updateData);
        }

        if (!updateData.isEmpty()) {
            updateItemsAndInvalidateCache(playerId, updateData);
        }
        return true;
    }

    private void swapItemsBetweenSlots(List<ItemInstance> playerItems,
                                       ItemSlot slot1, ItemSlot slot2,
                                       List<ItemInstance> updateData) {
        Optional<ItemInstance> itemInSlot1 = findItemInSlot(playerItems, slot1.getId());
        Optional<ItemInstance> itemInSlot2 = findItemInSlot(playerItems, slot2.getId());

        validateSwap(itemInSlot1, slot2);
        validateSwap(itemInSlot2, slot1);

        itemInSlot1.ifPresent(instance -> {
            instance.setSlotId(slot2.getId());
            updateData.add(instance);
        });
        itemInSlot2.ifPresent(instance -> {
            instance.setSlotId(slot1.getId());
            updateData.add(instance);
        });
    }

    private void validateSwap(Optional<ItemInstance> itemOpt, ItemSlot targetSlot) {
        itemOpt.ifPresent(instance -> {
            Optional<ItemTemplate> template = itemTemplateRepository.findTemplateById(instance.getTemplateId());
            if (!template.isPresent() || !targetSlot.canInput(template.get())) {
                throw new InvalidItemOperationException(
                        String.format("Item %s cannot be placed in slot %d",
                                instance.getId(), targetSlot.getId()));
            }
        });
    }

    private void validateSlotCapacity(List<ItemSlot> slots1, List<ItemSlot> slots2) {
        if (slots1.size() != slots2.size()) {
            throw new InvalidItemOperationException("Slot types have different capacities");
        }
    }

    // region ========== 道具查询方法 ==========

    @Override
    public List<ItemSlot> findItem(String playerId, int itemTemplateId, Map<String, Object> attributes) {
        return withLock(() -> {
            List<ItemInstance> playerItems = getPlayerItems(playerId);
            return playerItems.stream()
                    .filter(instance -> instance.getTemplateId() == itemTemplateId)
                    .filter(instance -> attributesMet(instance, attributes))
                    .map(instance -> itemSlotRepository.findById(instance.getSlotId()))
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .collect(Collectors.toList());
        });
    }

    @Override
    public List<ItemInstance> needGather(String playerId, SlotType slotType) {
        if (!validateNeedGatherParams(playerId, slotType)) {
            return Collections.emptyList();
        }
        return withLock(() -> {
            List<ItemInstance> slotItems = getPlayerItemsBySlotType(playerId, slotType);
            return slotItems.stream()
                    .filter(Objects::nonNull)
                    .filter(this::isItemNeedGather)
                    .collect(Collectors.toList());
        });
    }

    private boolean isItemNeedGather(ItemInstance instance) {
        Optional<ItemTemplate> template = itemTemplateRepository.findTemplateById(instance.getTemplateId());
        return template.isPresent()
                && template.get().isGather()
                && !instance.isGather();
    }

    private boolean attributesMet(ItemInstance instance, Map<String, Object> attributes) {
        if (attributes == null || attributes.isEmpty()) {
            return true;
        }
        if (instance.getAttributes() == null) {
            return false;
        }
        return attributes.entrySet().stream().allMatch(entry -> {
            Object instanceValue = instance.getAttributes().get(entry.getKey());
            if (instanceValue == null) return false;

            if (entry.getValue() instanceof Integer && instanceValue instanceof Integer) {
                return ((Integer) instanceValue) >= ((Integer) entry.getValue());
            } else if (entry.getValue() instanceof String && instanceValue instanceof String) {
                return ((String) instanceValue).equals(entry.getValue());
            }
            return false;
        });
    }
    // endregion

    // region ========== 增删改操作 ==========

    @Override
    public boolean updateItem(String playerId, ItemInstance instance) {
        validateUpdateParams(playerId, instance);
        return withLock(() -> {
            if (!validateItemOwnership(playerId, instance)) {
                return false;
            }
            validateItemSlotAndTemplate(instance);
            itemInstanceRepository.save(instance);
            invalidatePlayerCache(playerId);
            return true;
        });
    }

    @Override
    public boolean updateItems(String playerId, List<ItemInstance> instances) {
        validateBatchUpdateParams(playerId, instances);
        return withLock(() -> {
            Set<Integer> playerItemIds = getPlayerItemIds(playerId);
            boolean allUpdated = true;
            for (ItemInstance instance : instances) {
                if (instance == null) continue;
                if (!playerItemIds.contains(instance.getId())) {
                    allUpdated = false;
                    continue;
                }
                try {
                    validateItemSlotAndTemplate(instance);
                    itemInstanceRepository.save(instance);
                } catch (Exception e) {
                    allUpdated = false;
                }
            }
            invalidatePlayerCache(playerId);
            return allUpdated;
        });
    }

    @Override
    public ItemInstance createItem(String playerId, int templateId, int slotId, int stack) {
        validateCreateParams(playerId, templateId, slotId);
        return withLock(() -> {
            ItemSlot slot = validateAndGetSlot(slotId);
            ItemTemplate template = validateAndGetTemplate(templateId);
            validateItemPlacement(slot, template);

            ItemInstance instance = createNewItemInstance(playerId, templateId, slotId, stack);
            ItemInstance savedInstance = itemInstanceRepository.save(instance);
            invalidatePlayerCache(playerId);
            return savedInstance;
        });
    }

    private ItemInstance createNewItemInstance(String playerId, int templateId, int slotId, int stack) {
        ItemTemplate template = validateAndGetTemplate(templateId);
        ItemInstance instance = new ItemInstance();
        instance.setPlayerId(playerId);
        instance.setName(template.getName());
        instance.setTemplateId(templateId);
        instance.setSlotId(slotId);
        instance.setStack(stack);
        instance.setDurability(1);
        instance.setDurabilityEmptyDelete(false);
        instance.setBound(false);
        instance.setGather(template.isGather());
        return instance;
    }

    @Override
    public boolean deleteItem(String playerId, int instanceId) {
        validateDeleteParams(playerId);
        return withLock(() -> {
            Optional<ItemInstance> instance = itemInstanceRepository.findById(instanceId);
            if (!instance.isPresent() || !instance.get().getPlayerId().equals(playerId)) {
                return false;
            }
            itemInstanceRepository.delete(instanceId);
            invalidatePlayerCache(playerId);
            return true;
        });
    }

    @Override
    public boolean deleteItems(String playerId, List<Integer> instanceIds) {
        if (instanceIds == null || instanceIds.isEmpty()) {
            return false;
        }
        return withLock(() -> {
            boolean allDeleted = true;
            for (int instanceId : instanceIds) {
                if (!deleteItem(playerId, instanceId)) {
                    allDeleted = false;
                }
            }
            return allDeleted;
        });
    }

    @Override
    public List<ItemInstance> syncSlotItems(String playerId, List<SlotItem> slotItems, SlotType slotType) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        Objects.requireNonNull(slotItems, "Slot items cannot be null");
        Objects.requireNonNull(slotType, "Slot type cannot be null");

        return withLock(() -> {
            List<ItemSlot> slots = getSortedSlots(slotType);
            List<ItemInstance> currentItems = getPlayerItemsBySlotType(playerId, slotType);
            List<ItemInstance> result = new ArrayList<>();
            List<ItemInstance> toUpdate = new ArrayList<>();
            List<Integer> toDelete = new ArrayList<>();

            // 1. 首先处理所有槽位同步逻辑
            for (int i = 0; i < slots.size(); i++) {
                ItemSlot slot = slots.get(i);
                SlotItem config = i < slotItems.size() ? slotItems.get(i) : null;
                ItemInstance current = i < currentItems.size() ? currentItems.get(i) : null;

                processSlotSync(playerId, slot, config, current, toUpdate, toDelete, result);
            }

            // 2. 执行删除操作
            if (!toDelete.isEmpty()) {
                deleteItems(playerId, toDelete);
            }

            // 3. 执行更新/创建操作
            if (!toUpdate.isEmpty()) {
                // 分离出新实例和已有实例
                List<ItemInstance> newInstances = toUpdate.stream()
                        .filter(inst -> inst.getId() == 0)
                        .collect(Collectors.toList());
                List<ItemInstance> existingInstances = toUpdate.stream()
                        .filter(inst -> inst.getId() != 0)
                        .collect(Collectors.toList());

                // 保存新实例并获取其ID
                if (!newInstances.isEmpty()) {
                    for (ItemInstance instance : newInstances) {
                        ItemInstance savedInstance = itemInstanceRepository.save(instance);
                        // 更新result中的对应实例
                        updateResultWithSavedInstance(result, savedInstance);
                    }
                    invalidatePlayerCache(playerId);
                }

                // 更新已有实例
                if (!existingInstances.isEmpty()) {
                    updateItems(playerId, existingInstances);
                }
            }

            return result;
        });
    }

    /**
     * 更新结果列表中的临时实例为已保存的实例
     */
    private void updateResultWithSavedInstance(List<ItemInstance> result, ItemInstance savedInstance) {
        for (int i = 0; i < result.size(); i++) {
            ItemInstance instance = result.get(i);
            if (instance != null && instance.getId() == 0 &&
                    instance.getSlotId() == savedInstance.getSlotId()) {
                result.set(i, savedInstance);
                break;
            }
        }
    }

    private void processSlotSync(String playerId, ItemSlot slot,
                                 SlotItem config, ItemInstance current,
                                 List<ItemInstance> toUpdate, List<Integer> toDelete,
                                 List<ItemInstance> result) {
        // 情况1：配置为null，清空槽位
        if (config == null || config.getName() == null) {
            if (current != null) {
                toDelete.add(current.getId());
            }
            result.add(null);
            return;
        }

        // 情况2：当前没有实例，创建新实例
        if (current == null) {
            ItemTemplate template = findTemplateByName(config.getName())
                    .orElseThrow(() -> new ItemNotFoundException("Template not found: " + config.getName()));

            ItemInstance newInstance = createNewItemInstance(
                    playerId,
                    template.getId(),
                    slot.getId(),
                    config.getStack());

            toUpdate.add(newInstance);
            result.add(newInstance);
            return;
        }

        // 情况3：检查名称是否匹配
        boolean nameMatches = isNameMatch(current, config.getName());

        if (!nameMatches) {
            // 名称不匹配，替换为新实例
            ItemTemplate template = findTemplateByName(config.getName())
                    .orElseThrow(() -> new ItemNotFoundException("Template not found: " + config.getName()));

            ItemInstance newInstance = createNewItemInstance(
                    playerId,
                    template.getId(),
                    slot.getId(),
                    config.getStack());

            toDelete.add(current.getId());
            toUpdate.add(newInstance);
            result.add(newInstance);
        } else if (current.getStack() != config.getStack()) {
            // 名称匹配但数量不同，更新数量
            current.setStack(config.getStack());
            toUpdate.add(current);
            result.add(current);
        } else {
            // 完全匹配，只收集gather=false的
            result.add(current);
        }
    }

    private boolean isNameMatch(ItemInstance item, String expectedName) {
        if (item == null) return expectedName == null;
        return itemTemplateRepository.findTemplateById(item.getTemplateId())
                .map(t -> expectedName.equals(t.getName()))
                .orElse(false);
    }

    private Optional<ItemTemplate> findTemplateByName(String name) {
        return itemTemplateRepository.findAllTemplates().stream()
                .filter(t -> name.equals(t.getName()))
                .findFirst();
    }

    // region ========== 验证方法 ==========

    private boolean validateItemOwnership(String playerId, ItemInstance instance) {
        Optional<ItemInstance> existing = itemInstanceRepository.findById(instance.getId());
        return existing.isPresent() && existing.get().getPlayerId().equals(playerId);
    }

    private void validateItemSlotAndTemplate(ItemInstance instance) {
        ItemSlot slot = itemSlotRepository.findById(instance.getSlotId())
                .orElseThrow(() -> new SlotOperationException("Slot not found: " + instance.getSlotId()));

        ItemTemplate template = itemTemplateRepository.findTemplateById(instance.getTemplateId())
                .orElseThrow(() -> new ItemNotFoundException("Template not found: " + instance.getTemplateId()));

        if (!slot.canInput(template)) {
            throw new InvalidItemOperationException(
                    "Item type " + template.getType() + " cannot be placed in slot " + instance.getSlotId());
        }
    }

    private Set<Integer> getPlayerItemIds(String playerId) {
        return getPlayerItems(playerId).stream()
                .map(ItemInstance::getId)
                .collect(Collectors.toSet());
    }

    private void validateMoveParams(String playerId, int instanceId, int targetSlotId) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        if (instanceId <= 0) throw new IllegalArgumentException("Invalid slot instanceId");
        if (targetSlotId <= 0) throw new IllegalArgumentException("Invalid slot targetSlotId");
    }

    private void validateHandOffParams(String playerId, SlotType slotType1, SlotType slotType2) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        Objects.requireNonNull(slotType1, "Slot type 1 cannot be null");
        Objects.requireNonNull(slotType2, "Slot type 2 cannot be null");
    }

    private boolean validateNeedGatherParams(String playerId, SlotType slotType) {
        return playerId != null && slotType != null;
    }

    private void validateUpdateParams(String playerId, ItemInstance instance) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        Objects.requireNonNull(instance, "Instance cannot be null");
    }

    private void validateBatchUpdateParams(String playerId, List<ItemInstance> instances) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        Objects.requireNonNull(instances, "Instances cannot be null");
    }

    private void validateCreateParams(String playerId, int templateId, int slotId) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
        if (templateId <= 0 || slotId <= 0) {
            throw new IllegalArgumentException("Invalid template or slot ID");
        }
    }

    private void validateDeleteParams(String playerId) {
        Objects.requireNonNull(playerId, "Player ID cannot be null");
    }

    private ItemSlot validateAndGetSlot(int slotId) {
        return itemSlotRepository.findById(slotId)
                .orElseThrow(() -> new SlotOperationException("Slot not found: " + slotId));
    }

    private ItemTemplate validateAndGetTemplate(int templateId) {
        return itemTemplateRepository.findTemplateById(templateId)
                .orElseThrow(() -> new ItemNotFoundException("Template not found: " + templateId));
    }

    private void validateItemPlacement(ItemSlot slot, ItemTemplate template) {
        if (!slot.canInput(template)) {
            throw new InvalidItemOperationException(
                    "Item type " + template.getType() + " cannot be placed in slot " + slot.getId());
        }
    }
    // endregion
}